package com.zusmart.base.network.support;

import com.zusmart.base.future.FutureListener;
import com.zusmart.base.network.ChannelContext;
import com.zusmart.base.network.ChannelContextFuture;
import com.zusmart.base.network.ChannelContextManager;
import com.zusmart.base.network.handler.ChannelContextHandlerChain;
import com.zusmart.base.network.handler.support.DefaultChannelContextHandlerChain;
import com.zusmart.base.network.message.Message;
import com.zusmart.base.network.message.MessageFuture;
import com.zusmart.base.util.Assert;

public abstract class AbstractChannelContext implements ChannelContext {

	private long registTime;
	private long activeTime;

	private long readerDataTotal;
	private long readerDataCount;
	private long readerActiveTime;

	private long writerDataTotal;
	private long writerDataCount;
	private long writerActiveTime;

	private final boolean serverSide;
	private final String contextCode;
	private final ChannelContextManager contextManager;
	private final ChannelContextHandlerChain handlerChain;

	protected AbstractChannelContext(boolean serverSide, String contextCode,ChannelContextManager contextManager) {
		Assert.isBlank(contextCode, "context code must not be blank");
		Assert.isNull(contextManager, "context manager must not be null");
		this.serverSide = serverSide;
		this.contextCode = contextCode;
		this.contextManager = contextManager;
		this.handlerChain = this.createChannelContextHandlerChain();
	}

	@Override
	public long getRegistTime() {
		return this.registTime;
	}

	@Override
	public long getActiveTime() {
		return this.activeTime;
	}

	@Override
	public long getReaderActiveTime() {
		return this.readerActiveTime;
	}

	@Override
	public long getWriterActiveTime() {
		return this.writerActiveTime;
	}

	@Override
	public long getReaderDataTotal() {
		return this.readerDataTotal;
	}

	@Override
	public long getWriterDataTotal() {
		return this.writerDataTotal;
	}

	@Override
	public long getReaderDataCount() {
		return this.readerDataCount;
	}

	@Override
	public long getWriterDataCount() {
		return this.writerDataCount;
	}

	@Override
	public boolean isServerSide() {
		return this.serverSide == true;
	}

	@Override
	public boolean isClientSide() {
		return this.serverSide == false;
	}

	@Override
	public String getContextCode() {
		return this.contextCode;
	}
	
	@Override
	public ChannelContextManager getChannelContextManager() {
		return this.contextManager;
	}

	@Override
	public ChannelContextHandlerChain getChannelContextHandlerChain() {
		return this.handlerChain;
	}

	@Override
	public MessageFuture writeAndFlush(Message message) {
		final MessageFuture messageFuture = this.write(message);
		final ChannelContextFuture contextFuture = this.flush();
		final MessageFuture future = new MessageFuture(this, message);
		messageFuture.attachListener(new FutureListener<MessageFuture>() {
			@Override
			public void execute(MessageFuture f) throws Exception {
				if (!f.isSuccessed()) {
					future.setFailure(f.getFailureCause());
				}
			}
		});
		contextFuture.attachListener(new FutureListener<ChannelContextFuture>() {
			@Override
			public void execute(ChannelContextFuture f) throws Exception {
				if (f.isSuccessed()) {
					future.setSuccess();
				} else {
					future.setFailure(f.getFailureCause());
				}
			}
		});
		return future;
	}
	
	@Override
	public String toString() {
		if (this.isServerSide()) {
			return String.format("[Opened:%s][ServerSide:%s] %s -> %s", this.isOpen(), true, this.getServerAddress(), this.getClientAddress());
		} else {
			return String.format("[Opened:%s][ServerSide:%s] %s -> %s", this.isOpen(), false, this.getClientAddress(), this.getServerAddress());
		}
	}

	protected void updateOnRegist() {
		this.registTime = System.currentTimeMillis();
		this.activeTime = this.registTime;
	}

	protected void updateOnReader(long total) {
		this.activeTime = System.currentTimeMillis();
		this.readerActiveTime = this.activeTime;
		this.readerDataTotal += total;
		this.readerDataCount++;
	}

	protected void updateOnWriter(long total) {
		this.activeTime = System.currentTimeMillis();
		this.writerActiveTime = this.activeTime;
		this.writerDataTotal += total;
		this.writerDataCount++;
	}

	protected ChannelContextHandlerChain createChannelContextHandlerChain() {
		return new DefaultChannelContextHandlerChain(this);
	}

}